package tech.xs.sys.sync.mq;


import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import tech.xs.common.framework.domain.constant.mq.QueueNames;
import tech.xs.common.framework.domain.entity.BaseEntity;
import tech.xs.common.framework.domain.exception.BaseException;
import tech.xs.common.framework.domain.enums.DataSourceTypeEnum;
import tech.xs.common.domain.enums.YesNoEnum;
import tech.xs.common.framework.init.dict.domain.bo.sync.SyncChildDictBo;
import tech.xs.common.framework.init.dict.domain.bo.sync.SyncChildDictDataBo;
import tech.xs.common.framework.init.dict.domain.bo.sync.SyncDictBo;
import tech.xs.common.framework.init.dict.domain.bo.sync.SyncDictDataBo;
import tech.xs.common.framework.mq.MqMessageListener;
import tech.xs.common.framework.util.BaseEntityUtil;
import tech.xs.common.util.SyncUtil;
import tech.xs.sys.domain.entity.SysChildDict;
import tech.xs.sys.domain.entity.SysChildDictData;
import tech.xs.sys.domain.entity.SysDict;
import tech.xs.sys.domain.entity.SysDictData;
import tech.xs.sys.service.SysChildDictDataService;
import tech.xs.sys.service.SysChildDictService;
import tech.xs.sys.service.SysDictDataService;
import tech.xs.sys.service.SysDictService;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 字典消费者
 */
@Component
@Slf4j
public class DictConsumer extends MqMessageListener<ArrayList<SyncDictBo>> {

    private static final String LOCK_SYS_DICT_KEY = "lock:sys:sync:dict";

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private SysDictService sysDictService;
    @Resource
    private SysDictDataService sysDictDataService;
    @Resource
    private SysChildDictService sysChildDictService;
    @Resource
    private SysChildDictDataService sysChildDictDataService;

    @Override
    public String getQueueName() {
        return QueueNames.SYS_SYNC_DICT;
    }

    /**
     * 同步字典数据到数据库中
     */
    public void onMessage(ArrayList<SyncDictBo> dictList) throws Exception {
        String content = JSON.toJSONString(dictList);
        if (CollUtil.isEmpty(dictList)) {
            return;
        }
        RLock lock = getSyncDictLock();
        try {
            log.info("[MQ消费者][字典初始化][开始] {}", content);
            String module = dictList.get(0).getModule();
            List<SysDict> sysDicts = syncDict(module, dictList);
            syncDictData(sysDicts);
            syncChildDict(sysDicts);
            syncChildDictData(sysDicts);
            log.info("[MQ消费者][字典初始化][size:{}]完成", dictList.size());
        } catch (Exception e) {
            log.info("[MQ消费者][字典初始化][异常] {}", content);
            throw e;
        } finally {
            lock.unlock();
        }
        for (SyncDictBo syncDictBo : dictList) {
            sysDictService.reloadDictCache(syncDictBo.getCode());
        }
    }

    private void syncChildDictData(List<SysDict> dictList) {
        List<SysChildDictData> addList = new ArrayList<>();
        List<SysChildDictData> updateList = new ArrayList<>();
        List<SysChildDictData> deleteList = new ArrayList<>();
        List<SysChildDictData> sourceList = new ArrayList<>();

        setChildDictData(dictList, sourceList);
        comparisonChildDictData(dictList, sourceList, addList, updateList, deleteList);

        if (!addList.isEmpty()) {
            for (int i = 0; i < addList.size(); i++) {
                SysChildDictData childDictData = addList.get(i);
                SysChildDictData dbChildDictData = sysChildDictDataService.getOne(Wrappers.<SysChildDictData>lambdaQuery().select(BaseEntity::getId)
                        .eq(SysChildDictData::getChildDictId, childDictData.getChildDictId())
                        .eq(SysChildDictData::getDictId, childDictData.getDictId())
                        .eq(SysChildDictData::getDictDataId, childDictData.getDictDataId())
                );
                if (dbChildDictData != null) {
                    log.warn("[MQ消费者][字典初始化][手工添加子字典数据与系统子字典数据重复][替换手工添加子字典数据][" + dbChildDictData.getDictId() + "]");
                    childDictData.setId(dbChildDictData.getId());
                    updateList.add(childDictData);
                    addList.remove(i);
                    i--;
                }
            }
            if (!addList.isEmpty()) {
                sysChildDictDataService.saveBatch(addList, 300);
            }
        }
        if (!updateList.isEmpty()) {
            sysChildDictDataService.updateBatchById(updateList, 300);
        }
        if (!deleteList.isEmpty()) {
            sysChildDictDataService.removeBatchByIds(BaseEntityUtil.toIdList(deleteList), 300);
        }

    }

    private void comparisonChildDictData(List<SysDict> dictList, List<SysChildDictData> sourceList, List<SysChildDictData> addList, List<SysChildDictData> updateList, List<SysChildDictData> deleteList) {
        List<Long> dictIds = BaseEntityUtil.toIdList(dictList);
        List<SysChildDictData> dbList = sysChildDictDataService.list(Wrappers.<SysChildDictData>lambdaQuery()
                .in(SysChildDictData::getDictId, dictIds)
                .eq(SysChildDictData::getSource, DataSourceTypeEnum.SystemGeneration)
        );

        SyncUtil.contrast(sourceList, dbList, addList, updateList, deleteList, new SyncUtil.Compare<>() {
            @Override
            public boolean compare(SysChildDictData o1, SysChildDictData o2) {
                return o1.getDictId().equals(o2.getDictId()) && o1.getChildDictId().equals(o2.getChildDictId()) && o1.getDictDataId().equals(o2.getDictDataId());
            }

            @Override
            public SysChildDictData sourceToTarget(SysChildDictData source, SysChildDictData target) {
                source.setId(target.getId());
                return source;
            }
        });
    }

    private void setChildDictData(List<SysDict> dictList, List<SysChildDictData> sourceList) {
        for (SysDict dict : dictList) {
            List<SysChildDict> childDicts = dict.getChildDicts();
            if (childDicts == null) {
                continue;
            }
            for (SysChildDict childDict : childDicts) {
                List<SysChildDictData> datas = childDict.getDatas();
                if (datas == null) {
                    continue;
                }
                for (SysChildDictData data : datas) {
                    data.setDictId(dict.getId());
                    data.setChildDictId(childDict.getId());
                    data.setDictDataId(data.getDictData().getId());
                }
                sourceList.addAll(datas);
            }
        }

    }

    private void syncChildDict(List<SysDict> dictList) {
        List<Long> dictIds = BaseEntityUtil.toIdList(dictList);
        List<SysChildDict> dbList = sysChildDictService.list(Wrappers.<SysChildDict>lambdaQuery()
                .in(SysChildDict::getDictId, dictIds)
                .eq(SysChildDict::getSource, DataSourceTypeEnum.SystemGeneration)
        );
        List<SysChildDict> addList = new ArrayList<>();
        List<SysChildDict> updateList = new ArrayList<>();
        List<SysChildDict> deleteList = new ArrayList<>();
        List<SysChildDict> sourceList = new ArrayList<>();
        for (SysDict dict : dictList) {
            List<SysChildDict> datas = dict.getChildDicts();
            for (SysChildDict data : datas) {
                data.setDictId(dict.getId());
            }
            sourceList.addAll(datas);
        }
        SyncUtil.contrast(sourceList, dbList, addList, updateList, deleteList, new SyncUtil.Compare<>() {
            @Override
            public boolean compare(SysChildDict o1, SysChildDict o2) {
                return o1.getDictId().equals(o2.getDictId()) && o1.getCode().equals(o2.getCode());
            }

            @Override
            public SysChildDict sourceToTarget(SysChildDict source, SysChildDict target) {
                source.setId(target.getId());
                return source;
            }
        });

        if (!addList.isEmpty()) {
            for (int i = 0; i < addList.size(); i++) {
                SysChildDict dictData = addList.get(i);
                SysChildDict dbChildDict = sysChildDictService.getOne(Wrappers.<SysChildDict>lambdaQuery().select(BaseEntity::getId)
                        .eq(SysChildDict::getCode, dictData.getCode())
                        .eq(SysChildDict::getDictId, dictData.getDictId())
                );
                if (dbChildDict != null) {
                    log.warn("[MQ消费者][字典初始化][手工添加子字典与系统子字典重复][替换手工添加子字典][" + dbChildDict.getDictId() + ":" + dbChildDict.getCode() + "]");
                    dictData.setId(dbChildDict.getId());
                    updateList.add(dictData);
                    addList.remove(i);
                    i--;
                }
            }
            if (!addList.isEmpty()) {
                sysChildDictService.saveBatch(addList, 300);
            }
        }
        if (!updateList.isEmpty()) {
            sysChildDictService.updateBatchById(updateList, 300);
        }
        if (!deleteList.isEmpty()) {
            sysChildDictService.removeBatchByIds(BaseEntityUtil.toIdList(deleteList), 300);
        }
    }

    private void syncDictData(List<SysDict> dictList) {
        List<Long> dictIds = BaseEntityUtil.toIdList(dictList);
        List<SysDictData> dbList = sysDictDataService.list(Wrappers.<SysDictData>lambdaQuery()
                .in(SysDictData::getDictId, dictIds)
                .eq(SysDictData::getSource, DataSourceTypeEnum.SystemGeneration)
        );
        List<SysDictData> addList = new ArrayList<>();
        List<SysDictData> updateList = new ArrayList<>();
        List<SysDictData> deleteList = new ArrayList<>();
        List<SysDictData> sourceList = new ArrayList<>();
        for (SysDict dict : dictList) {
            List<SysDictData> datas = dict.getDatas();
            for (SysDictData data : datas) {
                data.setDictId(dict.getId());
            }
            sourceList.addAll(datas);
        }

        SyncUtil.contrast(sourceList, dbList, addList, updateList, deleteList, new SyncUtil.Compare<>() {
            @Override
            public boolean compare(SysDictData o1, SysDictData o2) {
                return o1.getDictId().equals(o2.getDictId()) && o1.getCode().equals(o2.getCode());
            }

            @Override
            public SysDictData sourceToTarget(SysDictData source, SysDictData target) {
                source.setId(target.getId());
                return source;
            }
        });
        if (!addList.isEmpty()) {
            for (int i = 0; i < addList.size(); i++) {
                SysDictData dictData = addList.get(i);
                SysDictData dbDictData = sysDictDataService.getOne(Wrappers.<SysDictData>lambdaQuery().select(BaseEntity::getId)
                        .eq(SysDictData::getCode, dictData.getCode())
                        .eq(SysDictData::getDictId, dictData.getDictId())
                );
                if (dbDictData != null) {
                    log.warn("[MQ消费者][字典初始化][手工添加字典数据与系统字典数据重复][替换手工添加字典][" + dbDictData.getDictId() + ":" + dbDictData.getCode() + "]");
                    dictData.setId(dbDictData.getId());
                    updateList.add(dictData);
                    addList.remove(i);
                    i--;
                }
            }
            if (!addList.isEmpty()) {
                sysDictDataService.saveBatch(addList, 300);
            }
        }
        if (!updateList.isEmpty()) {
            sysDictDataService.updateBatchById(updateList, 300);
        }
        if (!deleteList.isEmpty()) {
            sysDictDataService.removeBatchByIds(BaseEntityUtil.toIdList(deleteList), 300);
        }
    }

    /**
     * 同步系统字第
     *
     * @return 返回需要同步字典数据的的字典
     */
    private List<SysDict> syncDict(String moduleName, List<SyncDictBo> dictList) {
        List<SysDict> dbList = sysDictService.list(Wrappers.<SysDict>lambdaQuery()
                .eq(SysDict::getModule, moduleName)
                .eq(SysDict::getSource, DataSourceTypeEnum.SystemGeneration)
        );
        List<SysDict> addList = new ArrayList<>();
        List<SysDict> updateList = new ArrayList<>();
        List<SysDict> deleteList = new ArrayList<>();
        List<SysDict> sourceList = new ArrayList<>();
        for (SyncDictBo bo : dictList) {
            SysDict sysDict = new SysDict();
            sysDict.setCode(bo.getCode());
            sysDict.setName(bo.getName());
            sysDict.setValueType(bo.getValueType());
            sysDict.setModule(bo.getModule());
            sysDict.setSource(DataSourceTypeEnum.SystemGeneration);
            sysDict.setRemark(bo.getRemark());
            List<SyncDictDataBo> dataBos = bo.getDatas();
            List<SysDictData> datas = new ArrayList<>();
            Map<String, SysDictData> dictDataMap = new HashMap<>();
            for (SyncDictDataBo dataBo : dataBos) {
                SysDictData dictData = new SysDictData();
                dictData.setCode(dataBo.getCode());
                dictData.setName(dataBo.getName());
                dictData.setOrderValue(dataBo.getOrderValue());
                dictData.setStatus(YesNoEnum.YES);
                dictData.setSource(DataSourceTypeEnum.SystemGeneration);
                dictData.setRemark(dataBo.getRemark());
                dictDataMap.put(dictData.getCode(), dictData);
                datas.add(dictData);
            }
            sysDict.setDatas(datas);

            setDictChildDicts(bo, sysDict, dictDataMap);

            sourceList.add(sysDict);
        }

        SyncUtil.contrast(sourceList, dbList, addList, updateList, deleteList, new SyncUtil.Compare<>() {
            @Override
            public boolean compare(SysDict o1, SysDict o2) {
                return o1.getCode().equals(o2.getCode());
            }

            @Override
            public SysDict sourceToTarget(SysDict source, SysDict target) {
                source.setId(target.getId());
                return source;
            }
        });

        if (!addList.isEmpty()) {
            for (int i = 0; i < addList.size(); i++) {
                SysDict dict = addList.get(i);
                SysDict dbDict = sysDictService.getOne(Wrappers.<SysDict>lambdaQuery().select(BaseEntity::getId).eq(SysDict::getCode, dict.getCode()));
                if (dbDict != null) {
                    log.warn("[MQ消费者][字典初始化][手工添加字典与系统字典重复][替换手工添加字典][" + dict.getCode() + "]");
                    dict.setId(dbDict.getId());
                    updateList.add(dict);
                    addList.remove(i);
                    i--;
                }
            }
            if (!addList.isEmpty()) {
                sysDictService.saveBatch(addList, 300);
            }
        }
        if (!updateList.isEmpty()) {
            sysDictService.updateBatchById(updateList, 300);
        }
        if (!deleteList.isEmpty()) {
            List<Long> dictIds = BaseEntityUtil.toIdList(deleteList);
            sysDictDataService.remove(Wrappers.<SysDictData>lambdaQuery().in(SysDictData::getDictId, dictIds));
            sysDictService.removeBatchByIds(dictIds, 300);
        }
        List<SysDict> syncDictDatas = new ArrayList<>();
        syncDictDatas.addAll(addList);
        syncDictDatas.addAll(updateList);
        return syncDictDatas;
    }

    private void setDictChildDicts(SyncDictBo bo, SysDict sysDict, Map<String, SysDictData> dictDataMap) {
        List<SyncChildDictBo> childDictBos = bo.getChildDicts();
        if (childDictBos == null) {
            return;
        }

        List<SysChildDict> childDicts = new ArrayList<>();
        sysDict.setChildDicts(childDicts);
        for (SyncChildDictBo childDictBo : childDictBos) {
            SysChildDict childDict = new SysChildDict();
            childDict.setCode(childDictBo.getCode());
            childDict.setName(childDictBo.getName());
            childDict.setRemark(childDictBo.getRemark());
            childDict.setSource(DataSourceTypeEnum.SystemGeneration);
            childDicts.add(childDict);
            List<SyncChildDictDataBo> childDictDataBos = childDictBo.getDatas();
            if (childDictDataBos != null) {
                List<SysChildDictData> childDictDatas = new ArrayList<>();
                childDict.setDatas(childDictDatas);
                for (SyncChildDictDataBo childDictDataBo : childDictDataBos) {
                    SysChildDictData sysChildDictData = new SysChildDictData();
                    sysChildDictData.setSource(DataSourceTypeEnum.SystemGeneration);
                    sysChildDictData.setName(childDictDataBo.getName());
                    sysChildDictData.setOrderValue(childDictDataBo.getOrderValue());
                    sysChildDictData.setStatus(childDictDataBo.getStatus());
                    sysChildDictData.setRemark(childDictDataBo.getRemark());
                    sysChildDictData.setDictData(dictDataMap.get(childDictDataBo.getCode()));
                    childDictDatas.add(sysChildDictData);
                }
            }
        }
    }

    public RLock getSyncDictLock() throws InterruptedException {
        // 加锁,过期时间一分钟
        RLock lock = redissonClient.getLock(LOCK_SYS_DICT_KEY);
        if (lock.isLocked()) {
            if (!lock.tryLock(1, TimeUnit.MINUTES)) {
                throw new BaseException("获取字典同步锁失败");
            }
        }
        lock.lock(1, TimeUnit.MINUTES);
        return lock;
    }

}
